﻿Imports System
Imports System.CodeDom
Imports System.Diagnostics.CodeAnalysis
Imports System.Globalization
Imports System.Reflection

Namespace System.Workflow.Activities.Rules
    Friend Class EnumOperationMethodInfo
        Inherits MethodInfo
        ' Methods
        <SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")> _
        Public Sub New(ByVal lhs As Type, ByVal operation As CodeBinaryOperatorType, ByVal rhs As Type, ByVal isZero As Boolean)
            Me.op = operation
            Me.expectedParameters = New ParameterInfo() { New SimpleParameterInfo(lhs), New SimpleParameterInfo(rhs) }
            Dim flag As Boolean = ConditionHelper.IsNullableValueType(lhs)
            Dim flag2 As Boolean = ConditionHelper.IsNullableValueType(rhs)
            Me.lhsBaseType = If(flag, Nullable.GetUnderlyingType(lhs), lhs)
            Me.rhsBaseType = If(flag2, Nullable.GetUnderlyingType(rhs), rhs)
            If Me.lhsBaseType.IsEnum Then
                Me.lhsRootType = EnumHelper.GetUnderlyingType(Me.lhsBaseType)
            Else
                Me.lhsRootType = Me.lhsBaseType
            End If
            If Me.rhsBaseType.IsEnum Then
                Me.rhsRootType = EnumHelper.GetUnderlyingType(Me.rhsBaseType)
            Else
                Me.rhsRootType = Me.rhsBaseType
            End If
            Select Case Me.op
                Case CodeBinaryOperatorType.Add
                    If (Not Me.lhsBaseType.IsEnum OrElse Not rhs.IsEnum) Then
                        If Me.lhsBaseType.IsEnum Then
                            Me.resultBaseType = Me.lhsBaseType
                        Else
                            Me.resultBaseType = Me.rhsBaseType
                        End If
                        Exit Select
                    End If
                    Me.resultBaseType = Me.lhsRootType
                    Exit Select
                Case CodeBinaryOperatorType.Subtract
                    If (Not Me.rhsBaseType.IsEnum OrElse Not Me.lhsBaseType.IsEnum) Then
                        If Me.lhsBaseType.IsEnum Then
                            Me.resultRootType = Me.lhsRootType
                            If (isZero AndAlso (Not Me.rhsBaseType Is Me.lhsRootType)) Then
                                Me.resultBaseType = Me.lhsRootType
                            Else
                                Me.resultBaseType = Me.lhsBaseType
                            End If
                        Else
                            Me.resultRootType = Me.rhsRootType
                            If isZero Then
                                Me.resultBaseType = Me.rhsRootType
                            Else
                                Me.resultBaseType = Me.rhsBaseType
                            End If
                        End If
                    Else
                        Me.resultRootType = Me.rhsRootType
                        Me.resultBaseType = Me.rhsRootType
                    End If
                    Me.resultIsNullable = (flag OrElse flag2)
                    Me.resultType = If(Me.resultIsNullable, GetType(Nullable([Of] )).MakeGenericType(New Type() { Me.resultBaseType }), Me.resultBaseType)
                    Return
                Case CodeBinaryOperatorType.ValueEquality, CodeBinaryOperatorType.LessThan, CodeBinaryOperatorType.LessThanOrEqual, CodeBinaryOperatorType.GreaterThan, CodeBinaryOperatorType.GreaterThanOrEqual
                    Me.resultType = GetType(Boolean)
                    Return
                Case CodeBinaryOperatorType.BitwiseOr, CodeBinaryOperatorType.BitwiseAnd, CodeBinaryOperatorType.BooleanOr, CodeBinaryOperatorType.BooleanAnd
                    Return
                Case Else
                    Return
            End Select
            Me.resultIsNullable = (flag OrElse flag2)
            Me.resultType = If(Me.resultIsNullable, GetType(Nullable([Of] )).MakeGenericType(New Type() { Me.resultBaseType }), Me.resultBaseType)
        End Sub

        Public Overrides Function GetBaseDefinition() As MethodInfo
            Return Nothing
        End Function

        Public Overrides Function GetCustomAttributes(ByVal inherit As Boolean) As Object()
            Return New Object(0  - 1) {}
        End Function

        Public Overrides Function GetCustomAttributes(ByVal attributeType As Type, ByVal inherit As Boolean) As Object()
            Return New Object(0  - 1) {}
        End Function

        Public Overrides Function GetMethodImplementationFlags() As MethodImplAttributes
            Return MethodImplAttributes.CodeTypeMask
        End Function

        Public Overrides Function GetParameters() As ParameterInfo()
            Return Me.expectedParameters
        End Function

        <SuppressMessage("Microsoft.Performance", "CA1803:AvoidCostlyCallsWherePossible")> _
        Public Overrides Function Invoke(ByVal obj As Object, ByVal invokeAttr As BindingFlags, ByVal binder As Binder, ByVal parameters As Object(), ByVal culture As CultureInfo) As Object
            Dim obj2 As Object
            Dim literal As ArithmeticLiteral
            Dim literal2 As ArithmeticLiteral
            Dim literal3 As Literal
            Dim literal4 As Literal
            If (Me.lhsRootType Is Nothing) Then
                Me.lhsRootType = Enum.GetUnderlyingType(Me.lhsBaseType)
            End If
            If (Me.rhsRootType Is Nothing) Then
                Me.rhsRootType = Enum.GetUnderlyingType(Me.rhsBaseType)
            End If
            Select Case Me.op
                Case CodeBinaryOperatorType.Add
                    If ((Not parameters(0) Is Nothing) AndAlso (Not parameters(1) Is Nothing)) Then
                        literal = ArithmeticLiteral.MakeLiteral(Me.lhsRootType, parameters(0))
                        literal2 = ArithmeticLiteral.MakeLiteral(Me.rhsRootType, parameters(1))
                        obj2 = literal.Add(literal2)
                        obj2 = Executor.AdjustType(obj2.GetType, obj2, Me.resultBaseType)
                        If Me.resultIsNullable Then
                            obj2 = Activator.CreateInstance(Me.resultType, New Object() { obj2 })
                        End If
                        Return obj2
                    End If
                    Return Nothing
                Case CodeBinaryOperatorType.Subtract
                    If ((Not parameters(0) Is Nothing) AndAlso (Not parameters(1) Is Nothing)) Then
                        literal = ArithmeticLiteral.MakeLiteral(Me.resultRootType, Executor.AdjustType(Me.lhsRootType, parameters(0), Me.resultRootType))
                        literal2 = ArithmeticLiteral.MakeLiteral(Me.resultRootType, Executor.AdjustType(Me.rhsRootType, parameters(1), Me.resultRootType))
                        obj2 = literal.Subtract(literal2)
                        obj2 = Executor.AdjustType(obj2.GetType, obj2, Me.resultBaseType)
                        If Me.resultIsNullable Then
                            obj2 = Activator.CreateInstance(Me.resultType, New Object() { obj2 })
                        End If
                        Return obj2
                    End If
                    Return Nothing
                Case CodeBinaryOperatorType.ValueEquality
                    literal3 = Literal.MakeLiteral(Me.lhsRootType, parameters(0))
                    literal4 = Literal.MakeLiteral(Me.rhsRootType, parameters(1))
                    Return literal3.Equal(literal4)
                Case CodeBinaryOperatorType.LessThan
                    literal3 = Literal.MakeLiteral(Me.lhsRootType, parameters(0))
                    literal4 = Literal.MakeLiteral(Me.rhsRootType, parameters(1))
                    Return literal3.LessThan(literal4)
                Case CodeBinaryOperatorType.LessThanOrEqual
                    literal3 = Literal.MakeLiteral(Me.lhsRootType, parameters(0))
                    literal4 = Literal.MakeLiteral(Me.rhsRootType, parameters(1))
                    Return literal3.LessThanOrEqual(literal4)
                Case CodeBinaryOperatorType.GreaterThan
                    literal3 = Literal.MakeLiteral(Me.lhsRootType, parameters(0))
                    literal4 = Literal.MakeLiteral(Me.rhsRootType, parameters(1))
                    Return literal3.GreaterThan(literal4)
                Case CodeBinaryOperatorType.GreaterThanOrEqual
                    literal3 = Literal.MakeLiteral(Me.lhsRootType, parameters(0))
                    literal4 = Literal.MakeLiteral(Me.rhsRootType, parameters(1))
                    Return literal3.GreaterThanOrEqual(literal4)
            End Select
            Throw New RuleEvaluationException(String.Format(CultureInfo.CurrentCulture, Messages.BinaryOpNotSupported, New Object() { Me.op.ToString }))
        End Function

        Public Overrides Function IsDefined(ByVal attributeType As Type, ByVal inherit As Boolean) As Boolean
            Return True
        End Function


        ' Properties
        Public Overrides ReadOnly Property Attributes As MethodAttributes
            Get
                Return MethodAttributes.Static
            End Get
        End Property

        Public Overrides ReadOnly Property DeclaringType As Type
            Get
                Return GetType(Enum)
            End Get
        End Property

        Public Overrides ReadOnly Property MethodHandle As RuntimeMethodHandle
            Get
                Return New RuntimeMethodHandle
            End Get
        End Property

        Public Overrides ReadOnly Property Name As String
            Get
                Return "op_Enum"
            End Get
        End Property

        Public Overrides ReadOnly Property ReflectedType As Type
            Get
                Return Me.resultType
            End Get
        End Property

        Public Overrides ReadOnly Property ReturnType As Type
            Get
                Return Me.resultType
            End Get
        End Property

        Public Overrides ReadOnly Property ReturnTypeCustomAttributes As ICustomAttributeProvider
            Get
                Return Nothing
            End Get
        End Property


        ' Fields
        Private expectedParameters As ParameterInfo()
        Private lhsBaseType As Type
        Private lhsRootType As Type
        Private op As CodeBinaryOperatorType
        Private resultBaseType As Type
        Private resultIsNullable As Boolean
        Private resultRootType As Type
        Private resultType As Type
        Private rhsBaseType As Type
        Private rhsRootType As Type
    End Class
End Namespace

